{-# LANGUAGE PatternGuards #-}


module Preprocessor where

import System.IO
import Control.Monad
import Control.Monad.Trans.Error
import qualified Data.Set as Set


type Location = (String, Integer, Integer)


readLines :: FilePath -> IO [[(Char, Location)]]
------------------------------------------------
readLines path = liftM (annotate . lines) $ readFile path'
    where
      path' = path ++ ".lisp"

      annotateLine line lineIdx = [ (c, (path', lineIdx, n)) | (c, n) <- zip (line ++ "\n") [1..] ]
      annotate lines = [ annotateLine cs lineIdx | (cs, lineIdx) <- zip lines [1..] ]


startsWith :: Eq a => [a] -> [a] -> Maybe [a]
---------------------------------------------
startsWith []     ys     = Just ys
startsWith _      []     = Nothing
startsWith (x:xs) (y:ys)
    | x == y             = startsWith xs ys
    | otherwise          = Nothing


loadFile :: Set.Set FilePath -> FilePath -> IO [[(Char, Location)]]
-------------------------------------------------------------------
loadFile visited path
    | path `Set.member` visited = return []
    | otherwise                 = do input <- readLines path
                                     liftM concat $ mapM preprocessLine input
    where
      preprocessLine str
          | Just file <- isInclude str   = loadFile (Set.insert path visited) file
          | otherwise                    = return [str]

      isInclude = startsWith "#include " . map fst


load :: FilePath -> IO [(Char, Location)]
-----------------------------------------
load = liftM concat . loadFile Set.empty

